package system

import (
	"errors"
	"fmt"
	"strings"

	"soma-server/common"
	"soma-server/dto"
	system "soma-server/model/system"
	"soma-server/vo"

	"github.com/samber/lo"
	"github.com/thoas/go-funk"
)

type IRoleService interface {
	GetRoles(req *vo.RoleListRequest) ([]system.Role, int64, error)      // 获取角色列表
	GetRolesOptions() ([]dto.RolesOptions, error)                        // 获取角色下拉列表选项
	GetRolesByIds(roleIds []uint) ([]*system.Role, error)                // 根据角色ID获取角色
	CreateRole(role *system.Role) error                                  // 创建角色
	UpdateRoleById(roleId uint, role *system.Role) error                 // 更新角色
	GetRoleMenusById(roleId uint) ([]uint, error)                        // 获取角色的权限菜单
	UpdateRoleMenus(role *system.Role) error                             // 更新角色的权限菜单
	GetRoleApisByRoleKeyword(roleKeyword string) ([]uint, error)         // 根据角色关键字获取角色的权限接口
	UpdateRoleApis(roleKeyword string, reqRolePolicies [][]string) error // 更新角色的权限接口（先全部删除再新增）
	BatchDeleteRoleByIds(roleIds []uint) error                           // 删除角色
}

type RoleService struct {
}

func NewRoleService() IRoleService {
	return RoleService{}
}

// 获取角色列表
func (r RoleService) GetRoles(req *vo.RoleListRequest) ([]system.Role, int64, error) {
	var list []system.Role
	db := common.DB.Model(&system.Role{}).Order("created_at ASC")

	name := strings.TrimSpace(req.Name)
	if name != "" {
		db = db.Where("name LIKE ?", fmt.Sprintf("%%%s%%", name))
	}
	code := strings.TrimSpace(req.Code)
	if code != "" {
		db = db.Where("code LIKE ?", fmt.Sprintf("%%%s%%", code))
	}
	status := req.Status
	if status != 3 {
		db = db.Where("status = ?", status)
	}
	// currentPage > 0 且 pageSize > 0 才分页
	//记录总条数
	var total int64
	err := db.Count(&total).Error
	if err != nil {
		return list, total, err
	}
	currentPage := int(req.CurrentPage)
	pageSize := int(req.PageSize)
	if currentPage > 0 && pageSize > 0 {
		err = db.Offset((currentPage - 1) * pageSize).Limit(pageSize).Find(&list).Error
	} else {
		err = db.Find(&list).Error
	}
	return list, total, err
}

// 获取所有角色下拉列表项
func (r RoleService) GetRolesOptions() ([]dto.RolesOptions, error) {
	var roles []system.Role
	err := common.DB.Where("status = ?", 1).Order("created_at DESC").Find(&roles).Error
	rolesOptions := dto.ToRolesOptions(roles)
	return rolesOptions, err
}

// 根据角色ID获取角色
func (r RoleService) GetRolesByIds(roleIds []uint) ([]*system.Role, error) {
	var list []*system.Role
	err := common.DB.Where("id IN (?)", roleIds).Find(&list).Error
	return list, err
}

// 创建角色
func (r RoleService) CreateRole(role *system.Role) error {
	err := common.DB.Create(role).Error
	return err
}

// 更新角色
func (r RoleService) UpdateRoleById(roleId uint, role *system.Role) error {
	err := common.DB.Model(&system.Role{}).Where("id = ?", roleId).Updates(role).Error
	return err
}

// 获取角色的权限菜单
func (r RoleService) GetRoleMenusById(roleId uint) ([]uint, error) {
	var (
		role system.Role
		ids  []uint
		pids []uint
	)
	err := common.DB.Where("id = ?", roleId).Preload("Menus").First(&role).Error
	if err != nil {
		return nil, err
	}
	for _, menu := range role.Menus {
		if menu.ParentId != nil {
			parentId := menu.ParentId
			pids = append(pids, *parentId)
		}
		ids = append(ids, menu.ID)
	}
	// 树形控件勾选父节点会全选所有子节点，所以把父节点下如果有子节点，把父节点剔除掉
	left, _ := lo.Difference[uint](ids, pids)
	return left, err
}

// 更新角色的权限菜单
func (r RoleService) UpdateRoleMenus(role *system.Role) error {
	err := common.DB.Model(role).Association("Menus").Replace(role.Menus)
	return err
}

// 根据角色关键字获取角色的权限接口
func (r RoleService) GetRoleApisByRoleKeyword(roleKeyword string) ([]uint, error) {
	policies := common.CasbinEnforcer.GetFilteredPolicy(0, roleKeyword)

	// 获取所有接口
	var apis []*system.Api
	err := common.DB.Find(&apis).Error
	if err != nil {
		return nil, errors.New("获取角色的权限接口失败")
	}

	accessApis := make([]uint, 0)

	for _, policy := range policies {
		path := policy[1]
		method := policy[2]
		for _, api := range apis {
			if path == api.Path && method == api.Method {
				accessApis = append(accessApis, api.ID)
				break
			}
		}
	}

	return accessApis, err

}

// 更新角色的权限接口（先全部删除再新增）
func (r RoleService) UpdateRoleApis(roleCode string, reqRolePolicies [][]string) error {
	// 先获取path中的角色ID对应角色已有的police(需要先删除的)
	err := common.CasbinEnforcer.LoadPolicy()
	if err != nil {
		return errors.New("角色的权限接口策略加载失败")
	}
	rmPolicies := common.CasbinEnforcer.GetFilteredPolicy(0, roleCode)
	if len(rmPolicies) > 0 {
		isRemoved, err := common.CasbinEnforcer.RemovePolicies(rmPolicies)
		if !isRemoved {
			return errors.New("更新角色的权限接口失败: " + err.Error())
		}
	}
	isAdded, err := common.CasbinEnforcer.AddPolicies(reqRolePolicies)
	if !isAdded {
		if !funk.Contains(err.Error(), "empty") {
			return errors.New("更新角色的权限接口失败: " + err.Error())
		}
	}
	err = common.CasbinEnforcer.LoadPolicy()
	if err != nil {
		return errors.New("更新角色的权限接口成功，角色的权限接口策略加载失败")
	} else {
		return err
	}
}

// 删除角色
func (r RoleService) BatchDeleteRoleByIds(roleIds []uint) error {
	var roles []*system.Role
	err := common.DB.Where("id IN (?)", roleIds).Find(&roles).Error
	if err != nil {
		return err
	}
	err = common.DB.Select("Users", "Menus").Unscoped().Delete(&roles).Error
	// 删除成功就删除casbin policy
	if err == nil {
		for _, role := range roles {
			roleCode := role.Code
			rmPolicies := common.CasbinEnforcer.GetFilteredPolicy(0, roleCode)
			if len(rmPolicies) > 0 {
				isRemoved, _ := common.CasbinEnforcer.RemovePolicies(rmPolicies)
				if !isRemoved {
					return errors.New("删除角色成功, 删除角色关联权限接口失败")
				}
			}
		}

	}
	return err
}
